package com.hung.triple.registry;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.hung.triple.transport.Transporter;
import com.hung.triple.transport.TransporterPool;
import com.hung.triple.transport.netty4.NettyTransporter;
import com.hung.triple.transport.netty4.client.NettyConnectionFactory;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 存储所有服务提供者的地址
 *
 * @author Hung
 * @since 2022/9/18
 */
@Slf4j
public class ProviderAddressPool {

    /**
     * 服务Mao 通过key找到这个服务器的所有IP
     * group#serviceName:version -> providerIPList
     */
    public static final Map<String, Set<String>> ADDRESS_POOL = Maps.newConcurrentMap();


    public static void add(String group, String serviceName, String version, String... address) {
        add(group, serviceName, version, Lists.newArrayList(address));
    }

    public static void add(String group, String serviceName, String version, List<String> address) {
        String key = genKey(group, serviceName, version);
        Set<String> ips = ADDRESS_POOL.computeIfAbsent(key, ignore -> Sets.newConcurrentHashSet());
        ips.addAll(address);
    }

    public static Set<String> get(String group, String serviceName, String version) {
        String key = genKey(group, serviceName, version);
        return ADDRESS_POOL.get(key);
    }

    /**
     * 同步移除 providerIP
     */
    public static void remove(String group, String serviceName, String version, String address) {
        String key = genKey(group, serviceName, version);

        try {
            ADDRESS_POOL.get(key).remove(address);
        } catch (Exception ignore) {
        }

    }

    public static void removeAll(String group, String serviceName, String version) {
        String key = genKey(group, serviceName, version);

        try {
            ADDRESS_POOL.remove(key);
        } catch (Exception ignore) {
        }

    }

    //TODO ProviderAddressPool和TransporterPool的并发问题，应该怎么加锁才好
    public static synchronized void namingEventHandler(String group, String serviceName, String version, Set<String> address) {
        String key = genKey(group, serviceName, version);
        Set<String> rawSet = ADDRESS_POOL.get(key);
        //如果rawSet和Address都有，那就不变
        //如果rawSet有、Address没有，就删除这个，然后删除Transporter
        //如果rawSet没有，但Address有，就新建Transporter
        //所以选择双循环O(n)

        address.forEach(remoteAddress -> {
            if (!rawSet.contains(remoteAddress)) {
                String[] split = remoteAddress.split(":");
                Channel channel;
                try {
                    channel = NettyConnectionFactory.getClient().connect(split[0], Integer.parseInt(split[1]));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                //TODO 可以做个工厂生成Transporter
                rawSet.add(remoteAddress);
                Transporter transporter = new NettyTransporter(channel);
                TransporterPool.addTransporter(serviceName, remoteAddress, transporter);
            }
        });

        rawSet.forEach(remoteAddress -> {
            if (!address.contains(remoteAddress)) {
                rawSet.remove(remoteAddress);
                TransporterPool.removeTransporter(serviceName, remoteAddress);
            }
        });

    }

    public static Set<String> getAllProviderHosts(String serviceKey) {
        return ADDRESS_POOL.computeIfAbsent(serviceKey, ignore -> Sets.newHashSet());
    }

    public static String genKey(String group, String serviceName, String version) {
        return String.format("%s#%s:%s", group, serviceName, version);
    }
}
